using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace LSO
{
    public enum VariableType
    {
        Void = 0,
        Integer = 1,
        Float = 2,
        String = 3,
        Key = 4,
        Vector = 5,
        Rotation = 6,
        List = 7
    }

    public enum ArgType
    {
        Type,
        LType,
        RType,
        Integer,
        Float,
        String,
        StaticFrameOffset,
        LocalFrameOffset,
        CodeOffset,
        StateIndex,
        UserFunctionIndex,
        LibFunctionIndexByte,
        LibFunctionIndexTwoByte
    }

    public enum Events
    {
        state_entry = 0,
        state_exit = 1,
        touch_start = 2,
        touch = 3,
        touch_end = 4,
        collision_start = 5,
        collision = 6,
        collision_end = 7,
        land_collision_start = 8,
        land_collision = 9,
        land_collision_end = 10,
        timer = 11,
        listen = 12,
        on_rez = 13,
        sensor = 14,
        no_sensor = 15,
        control = 16,
        money = 17,
        email = 18,
        at_target = 19,
        not_at_target = 20,
        at_rot_target = 21,
        not_at_rot_target = 22,
        run_time_permissions = 23,
        changed = 24,
        attach = 25,
        dataserver = 26,
        link_message = 27,
        moving_start = 28,
        moving_end = 29,
        object_rez = 30,
        remote_data = 31,
        http_response = 32
    }

    class LSOAssembler
    {
        AssemblerStage Stage = new AssemblerStage();
        AssemblerSubstage Substage = new AssemblerSubstage();
        TokenType ExpectedForSubstage = new TokenType();
        TokenType ExpectedForSubstageChange = new TokenType();
        public string ErrorMessage;

        int StaticBlockIndex;
        int VectorIndex;
        int StateIndex;

        public LSOAssembly Assembly = new LSOAssembly();
        int[] GlobalVariableOffsets;
        List<string> GlobalVariableIdentifiers = new List<string>();
        int LocalVariableIndex;
        int LocalVariableIndexOffset;
        List<int> LocalVariableOffsets;
        List<string> LocalVariableIdentifiers = new List<string>();
        Dictionary<string, int> Labels = new Dictionary<string, int>();
        List<string> UserFunctionIdentifiers = new List<string>();
        List<string> StateIdentifiers = new List<string>();

        int HeapIndex = -1;
        int HeapOffset = 0;

        ArgType[] ExpectedArgTypes;
        int ExpectedArgIndex;
        byte LType;
        int InstructionOffset;
        int NextInstructionOffset;
        byte[] Code;
        int CodeLength;

        public class CodeForAssembly
        {
            public int CallFrameSize;
            public byte[] Code;
        }

        Events ThisEvent;
        int ThisCallFrameSize;
        Dictionary<Events, CodeForAssembly> FinishedCodeChunks = new Dictionary<Events, CodeForAssembly>();

        Dictionary<string, VariableType> TypeByName = new Dictionary<string, VariableType>();
        Dictionary<string, Events> EventByName = new Dictionary<string, Events>();
        string[] Builtins;


        public class Token
        {
            public TokenType Type = TokenType.Unknown;
            public string Text;

            public Token(TokenType type, string text)
            {
                Type = type;
                Text = text;
            }
        }

        public enum AssemblerStage
        {
            Done,
            Globals,
            DefaultState,
            State,
            StateContinue,
            EventHandler
        }

        public enum AssemblerSubstage
        {
            None,
            Global_Type,
            Global_Identifier,
            Global_Assignment,
            Global_Literal_Initial,
            Global_Literal_Vector_Item,
            Global_Literal_Vector_End,
            State_Keyword,
            State_Identifier,
            State_Begin,
            State_Next_EventHandler,
            Event_Args_Begin,
            Event_Arg_Type,
            Event_Arg_Identifier,
            Event_Begin,
            Event_Innards,
            Event_Local_Identifier,
            Event_Instruction_Args
        }

        public enum TokenType
        {
            Nothing = 0,
            Unknown = 1,
            EOF = 1 << 1,
            Type = 1 << 2,
            Identifier = 1 << 3,
            Assignment = 1 << 4,
            Literal = 1 << 5,
            VectorBegin = 1 << 6,
            VectorEnd = 1 << 7,
            StateDefault = 1 << 8,
            StateKeyword = 1 << 9,
            ScopeBegin = 1 << 10,
            ScopeEnd = 1 << 11,
            EventName = 1 << 12,
            EventArgsBegin = 1 << 13,
            EventArgsEnd = 1 << 14,
            Label = 1 << 15,
            Instruction = 1 << 16,
            String = 1 << 17
        }

        public delegate void LogMessageHandler(string message);
        public event LogMessageHandler OnLogMessage;

        void Log(string message)
        {
            if (OnLogMessage != null)
            {
                OnLogMessage(message);
            }
        }
        
        public int SizeOfVariableType(VariableType t)
        {
            switch (t)
            {
                case VariableType.Void:
                    return 0;
                case VariableType.Integer:
                    return 4;
                case VariableType.Float:
                    return 4;
                case VariableType.String:
                    return 4;
                case VariableType.Key:
                    return 4;
                case VariableType.Vector:
                    return 12;
                case VariableType.Rotation:
                    return 16;
                case VariableType.List:
                    return 4;
                default:
                    return 0;
            }
        }

        public string TokenTypeToString(TokenType t)
        {
            if ((int)t == 0)
                return "nothing";
            string output = "";
            int mask;
            bool already = false;
            for (int i = 0; i < 32; i++)
            {
                mask = 1 << i;
                if((mask & (int)t) != 0)
                {
                    if (already == true)
                        output += " or ";
                    else
                        already = true;
                    output += ((TokenType)mask).ToString();
                }
            }
            return output;
        }

        public void BeginStage(AssemblerStage new_stage)
        {
            Stage = new_stage;
            switch (Stage)
            {
                case AssemblerStage.Globals:
                    BeginSubstage(AssemblerSubstage.Global_Type);
                    break;
                case AssemblerStage.DefaultState:
                    StateIdentifiers.Add("default");
                    BeginSubstage(AssemblerSubstage.State_Begin);
                    break;
                case AssemblerStage.State:
                    BeginSubstage(AssemblerSubstage.State_Keyword);
                    break;
                case AssemblerStage.StateContinue:
                    BeginSubstage(AssemblerSubstage.State_Next_EventHandler);
                    break;
                case AssemblerStage.EventHandler:
                    BeginSubstage(AssemblerSubstage.Event_Args_Begin);
                    break;

                case AssemblerStage.Done:
                    BeginSubstage(AssemblerSubstage.None);
                    break;
                default:
                    break;
            }
        }

        public void BeginSubstage(AssemblerSubstage new_substage)
        {
            Substage = new_substage;
            switch (Substage)
            {
                case AssemblerSubstage.None:
                    ExpectedForSubstage = TokenType.Nothing;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;

                case AssemblerSubstage.Global_Type:
                    ExpectedForSubstage = TokenType.Type;
                    ExpectedForSubstageChange = TokenType.StateDefault;
                    break;
                case AssemblerSubstage.Global_Identifier:
                    ExpectedForSubstage = TokenType.Identifier;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;
                case AssemblerSubstage.Global_Assignment:
                    ExpectedForSubstage = TokenType.Assignment | TokenType.Type;
                    ExpectedForSubstageChange = TokenType.StateDefault;
                    break;
                case AssemblerSubstage.Global_Literal_Initial:
                    ExpectedForSubstage = TokenType.Literal | TokenType.VectorBegin | TokenType.String;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;
                case AssemblerSubstage.Global_Literal_Vector_Item:
                    ExpectedForSubstage = TokenType.Literal;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;
                case AssemblerSubstage.Global_Literal_Vector_End:
                    ExpectedForSubstage = TokenType.VectorEnd;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;

                case AssemblerSubstage.State_Keyword:
                    ExpectedForSubstage = TokenType.StateKeyword;
                    ExpectedForSubstageChange = TokenType.EOF;
                    break;
                case AssemblerSubstage.State_Identifier:
                    ExpectedForSubstage = TokenType.Identifier;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;
                case AssemblerSubstage.State_Begin:
                    ExpectedForSubstage = TokenType.ScopeBegin;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;
                case AssemblerSubstage.State_Next_EventHandler:
                    ExpectedForSubstage = TokenType.EventName;
                    ExpectedForSubstageChange = TokenType.ScopeEnd;
                    break;

                case AssemblerSubstage.Event_Args_Begin:
                    ExpectedForSubstage = TokenType.EventArgsBegin;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;
                case AssemblerSubstage.Event_Arg_Type:
                    ExpectedForSubstage = TokenType.Type;
                    ExpectedForSubstageChange = TokenType.EventArgsEnd;
                    break;
                case AssemblerSubstage.Event_Arg_Identifier:
                    ExpectedForSubstage = TokenType.Identifier;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;

                case AssemblerSubstage.Event_Begin:
                    ExpectedForSubstage = TokenType.ScopeBegin;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;
                case AssemblerSubstage.Event_Innards:
                    ExpectedForSubstage = TokenType.Type | TokenType.Label | TokenType.Instruction;
                    ExpectedForSubstageChange = TokenType.ScopeEnd;
                    break;
                case AssemblerSubstage.Event_Local_Identifier:
                    ExpectedForSubstage = TokenType.Identifier;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;
                case AssemblerSubstage.Event_Instruction_Args:
                    ExpectedForSubstage = TokenType.Identifier | TokenType.Label | TokenType.Literal | TokenType.Type | TokenType.String;
                    ExpectedForSubstageChange = TokenType.Nothing;
                    break;

                default:
                    Log("Substage not implemented!");
                    break;
            }
        }

        public bool IdentifierExists(string name)
        {
            if (GlobalVariableIdentifiers.Contains(name))
                return true;
            else if (LocalVariableIdentifiers.Contains(name))
                return true;
            else
                return false;
        }

        public bool LabelExists(string name)
        {
            if (Labels.ContainsKey(name))
                return true;
            else
                return false;
        }

        void CalculateGlobalVariableOffsets()
        {
            int count = Assembly.StaticBlocks.Count;
            int offset = 0;
            GlobalVariableOffsets = new int[count];
            for (int i = 0; i < count; i++)
            {
                GlobalVariableOffsets[i] = offset + Assembly.StaticBlocks[i].Header.Size;
                offset += Assembly.StaticBlocks[i].Size;
            }
            //foreach (int off in GlobalVariableOffsets)
            //{
            //    Log("Global @ " + off.ToString());
            //}
        }

        void FinishState()
        {
            Events e;
            LSOAssembly.StateBlock stateblock = new LSOAssembly.StateBlock();
            LSOAssembly.StateBlock.StateBlockHandler handler;
            LSOAssembly.EventHandlerCodeChunk chunk;
            for(int i = 0; i < 33; i++)
            {
                e = (Events)i;
                if (FinishedCodeChunks.ContainsKey(e))
                {
                    Log("Adding code chunk for " + e.ToString());
                    handler = new LSOAssembly.StateBlock.StateBlockHandler();
                    handler.CallFrameSize = FinishedCodeChunks[e].CallFrameSize;
                    stateblock.StateBlockHandlers.Add(handler);
                    chunk = new LSOAssembly.EventHandlerCodeChunk();
                    chunk.Code = FinishedCodeChunks[e].Code;
                    Assembly.EventHandlerCodeChunks.Add(chunk);
                }
            }
            Assembly.StateBlocks.Add(stateblock);
        }

        void FinishAssembly()
        {

        }

        public bool ReceiveToken(Token token)
        {
            if ((token.Type & (ExpectedForSubstage | ExpectedForSubstageChange)) == 0)
            {
                ErrorMessage = "Expected " + TokenTypeToString(ExpectedForSubstage | ExpectedForSubstageChange) + ", got " + token.Type.ToString() + ". (Stage: " + Stage.ToString() + ", Substage: " + Substage.ToString() + ")";
                return false;
            }
            else
            {
                if ((token.Type & ExpectedForSubstage) != 0)
                {
                    if (Stage == AssemblerStage.Globals)
                    {
                        if (Substage == AssemblerSubstage.Global_Type || Substage == AssemblerSubstage.Global_Assignment)
                        {
                            if (token.Type == TokenType.Type)
                            {
                                // Create a new global with this type
                                Assembly.StaticBlocks.Add(new LSOAssembly.StaticBlock());
                                StaticBlockIndex = Assembly.StaticBlocks.Count - 1;
                                VariableType gtype = TypeByName[token.Text];
                                Assembly.StaticBlocks[StaticBlockIndex].Header.TypeOfObject = gtype;
                                Assembly.StaticBlocks[StaticBlockIndex].Data = new byte[SizeOfVariableType(gtype)];
                                if (gtype == VariableType.String || gtype == VariableType.Key)
                                {
                                    // Create a HeapBlock
                                    if (HeapIndex >= 0)
                                        HeapOffset += Assembly.HeapBlocks[HeapIndex].Size;
                                    HeapIndex++;
                                    int ho = HeapOffset + 1;
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(ho)), 0, Assembly.StaticBlocks[StaticBlockIndex].Data, 0, 4);
                                    Assembly.HeapBlocks.Add(new LSOAssembly.HeapBlock(VariableType.String, new byte[] { 0x00 }));
                                }
                                BeginSubstage(AssemblerSubstage.Global_Identifier);
                            }
                            else if (token.Type == TokenType.Assignment)
                            {
                                // Do nothing but expect value
                                BeginSubstage(AssemblerSubstage.Global_Literal_Initial);
                            }
                        }
                        else if (Substage == AssemblerSubstage.Global_Identifier)
                        {
                            // Give global an identifier
                            if (IdentifierExists(token.Text))
                            {
                                ErrorMessage = "The identifier " + token.Text + " is already in use.";
                                return false;
                            }
                            else
                            {
                                // Create an identifier for the most recent global
                                GlobalVariableIdentifiers.Add(token.Text);
                                BeginSubstage(AssemblerSubstage.Global_Assignment);
                            }
                        }
                        else if (Substage == AssemblerSubstage.Global_Literal_Initial)
                        {
                            // Assign value to this global variable
                            if (token.Type == TokenType.Literal)
                            {
                                switch (Assembly.StaticBlocks[StaticBlockIndex].Header.TypeOfObject)
                                {
                                    case VariableType.Integer:
                                        int intvalue = int.Parse(token.Text);
                                        Assembly.StaticBlocks[StaticBlockIndex].Data = Helpers.BigEndian(BitConverter.GetBytes(intvalue));
                                        break;
                                    case VariableType.Float:
                                        float floatvalue = float.Parse(token.Text);
                                        Assembly.StaticBlocks[StaticBlockIndex].Data = Helpers.BigEndian(BitConverter.GetBytes(floatvalue));
                                        break;
                                    default:
                                        ErrorMessage = "Static block being assigned to wants a type that isn't implemented!";
                                        return false;
                                }
                                BeginSubstage(AssemblerSubstage.Global_Type);
                            }
                            else if (token.Type == TokenType.VectorBegin)
                            {
                                // Beginning a literal vector or rotation
                                switch (Assembly.StaticBlocks[StaticBlockIndex].Header.TypeOfObject)
                                {
                                    case VariableType.Vector:
                                        Assembly.StaticBlocks[StaticBlockIndex].Data = new byte[12];
                                        VectorIndex = 2;
                                        break;
                                    case VariableType.Rotation:
                                        Assembly.StaticBlocks[StaticBlockIndex].Data = new byte[16];
                                        VectorIndex = 3;
                                        break;
                                    default:
                                        break;
                                }
                                BeginSubstage(AssemblerSubstage.Global_Literal_Vector_Item);
                            }
                            else if (token.Type == TokenType.String)
                            {
                                byte[] heapstr = new byte[token.Text.Length + 1];
                                Buffer.BlockCopy(Encoding.ASCII.GetBytes(token.Text), 0, heapstr, 0, token.Text.Length);
                                heapstr[token.Text.Length] = (byte)0;
                                Assembly.HeapBlocks[HeapIndex].Data = heapstr;
                                Assembly.HeapBlocks[HeapIndex].TypeOfObject = VariableType.String;
                                BeginSubstage(AssemblerSubstage.Global_Type);
                            }
                        }
                        else if (Substage == AssemblerSubstage.Global_Literal_Vector_Item)
                        {
                            byte[] floatdata = Helpers.BigEndian(BitConverter.GetBytes(float.Parse(token.Text)));
                            Buffer.BlockCopy(floatdata, 0, Assembly.StaticBlocks[StaticBlockIndex].Data, VectorIndex * 4, 4);
                            VectorIndex--;
                            if (VectorIndex >= 0)
                            {
                                BeginSubstage(AssemblerSubstage.Global_Literal_Vector_Item);
                            }
                            else
                            {
                                BeginSubstage(AssemblerSubstage.Global_Literal_Vector_End);
                            }
                        }
                        else if (Substage == AssemblerSubstage.Global_Literal_Vector_End)
                        {
                            BeginSubstage(AssemblerSubstage.Global_Type);
                        }
                    }
                    else if (Stage == AssemblerStage.State || Stage == AssemblerStage.DefaultState || Stage == AssemblerStage.StateContinue)
                    {
                        if (Substage == AssemblerSubstage.State_Keyword)
                            BeginSubstage(AssemblerSubstage.State_Identifier);
                        else if (Substage == AssemblerSubstage.State_Identifier)
                        {
                            StateIdentifiers.Add(token.Text);
                            BeginSubstage(AssemblerSubstage.State_Begin);
                        }
                        else if (Substage == AssemblerSubstage.State_Begin)
                        {
                            Assembly.StatesFrameBlock.NumberOfStates++;
                            if (Stage == AssemblerStage.DefaultState)
                                StateIndex = 0;
                            else
                                StateIndex++;
                            FinishedCodeChunks = new Dictionary<Events, CodeForAssembly>();
                            // Create StatePointer for adding to event mask,
                            // but not pointers til much later
                            Assembly.StatesFrameBlock.StatePointers.Add(new LSOAssembly.StateFrameBlock.StatePointerBlock());
                            BeginSubstage(AssemblerSubstage.State_Next_EventHandler);
                        }
                        else if (Substage == AssemblerSubstage.State_Next_EventHandler)
                        {
                            // Create an event handler
                            Events eventnumber = EventByName[token.Text];
                            long mask = 1 << (int)eventnumber;
                            Log("Handler for event " + eventnumber.ToString());
                            ThisEvent = eventnumber;
                            if (StateIndex == 0)
                            {
                                Assembly.Header.NER |= mask;
                                if (ThisEvent == Events.state_entry)
                                    Assembly.Header.NCE |= mask;
                            }
                            Assembly.StatesFrameBlock.StatePointers[StateIndex].EventMask |= mask;
                            BeginStage(AssemblerStage.EventHandler);
                        }
                    }
                    else if (Stage == AssemblerStage.EventHandler)
                    {
                        if (Substage == AssemblerSubstage.Event_Args_Begin)
                        {
                            LocalVariableIndex = -1;
                            LocalVariableIndexOffset = 0;
                            LocalVariableIdentifiers = new List<string>();
                            LocalVariableOffsets = new List<int>();
                            ThisCallFrameSize = 0;
                            BeginSubstage(AssemblerSubstage.Event_Arg_Type);
                        }
                        else if (Substage == AssemblerSubstage.Event_Arg_Type)
                        {
                            // FIXME: Check correctness of type?
                            VariableType type = TypeByName[token.Text];
                            LocalVariableIndex++;
                            LocalVariableIdentifiers.Add("");
                            LocalVariableOffsets.Add(LocalVariableIndexOffset);
                            LocalVariableIndexOffset += SizeOfVariableType(type);
                            ThisCallFrameSize += SizeOfVariableType(type);
                            BeginSubstage(AssemblerSubstage.Event_Arg_Identifier);
                        }
                        else if (Substage == AssemblerSubstage.Event_Arg_Identifier)
                        {
                            if (IdentifierExists(token.Text))
                            {
                                ErrorMessage = "The identifier " + token.Text + " is already in use.";
                                return false;
                            }
                            else
                            {
                                LocalVariableIdentifiers[LocalVariableIndex] = token.Text;
                                BeginSubstage(AssemblerSubstage.Event_Arg_Type);
                            }
                        }
                        else if (Substage == AssemblerSubstage.Event_Begin)
                        {
                            InstructionOffset = 0;
                            Code = new byte[16384];
                            CodeLength = 0;
                            Labels = new Dictionary<string, int>();
                            BeginSubstage(AssemblerSubstage.Event_Innards);
                        }
                        else if (Substage == AssemblerSubstage.Event_Innards)
                        {
                            // Innards!
                            switch (token.Type)
                            {
                                case TokenType.Type:
                                    VariableType type = TypeByName[token.Text];
                                    LocalVariableIndex++;
                                    LocalVariableIdentifiers.Add("");
                                    LocalVariableOffsets.Add(LocalVariableIndexOffset);
                                    LocalVariableIndexOffset += SizeOfVariableType(type);
                                    ThisCallFrameSize += SizeOfVariableType(type);
                                    BeginSubstage(AssemblerSubstage.Event_Local_Identifier);
                                    break;
                                case TokenType.Label:
                                    if (LabelExists(token.Text))
                                    {
                                        ErrorMessage = "The label \"" + token.Text + "\" is defined twice in this scope";
                                        return false;
                                    }
                                    else
                                    {
                                        Labels.Add(token.Text, InstructionOffset);
                                        BeginSubstage(AssemblerSubstage.Event_Innards);
                                    }
                                    break;
                                case TokenType.Instruction:
                                    InstructionOffset = CodeLength;
                                    LSO.InstructionInfo inst = LSO.InstructionByName(token.Text);
                                    if (inst.Opcode == 0x90) NextInstructionOffset = CodeLength + 5;
                                    else if (inst.Opcode == 0x91 || inst.Opcode == 0x92) NextInstructionOffset = CodeLength + 6;
                                    Code[CodeLength] = inst.Opcode;
                                    CodeLength++;
                                    if (inst.NumberOfArgs > 0)
                                    {
                                        ExpectedArgTypes = inst.ArgTypes;
                                        ExpectedArgIndex = 0;
                                        BeginSubstage(AssemblerSubstage.Event_Instruction_Args);
                                    }
                                    else
                                    {
                                        BeginSubstage(AssemblerSubstage.Event_Innards);
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                        else if (Substage == AssemblerSubstage.Event_Local_Identifier)
                        {
                            if (IdentifierExists(token.Text))
                            {
                                ErrorMessage = "The identifier " + token.Text + " is already in use.";
                                return false;
                            }
                            else
                            {
                                LocalVariableIdentifiers[LocalVariableIndex] = token.Text;
                                BeginSubstage(AssemblerSubstage.Event_Innards);
                            }
                        }
                        else if (Substage == AssemblerSubstage.Event_Instruction_Args)
                        {
                            switch (ExpectedArgTypes[ExpectedArgIndex])
                            {
                                case ArgType.Type:
                                    Code[CodeLength] = (byte)TypeByName[token.Text];
                                    CodeLength++;
                                    break;
                                case ArgType.LType:
                                    int typenumber = (int)TypeByName[token.Text];
                                    LType = (byte)(typenumber << 4);
                                    break;
                                case ArgType.RType:
                                    byte barg = (byte)TypeByName[token.Text];
                                    barg |= LType;
                                    Code[CodeLength] = barg;
                                    CodeLength++;
                                    break;
                                case ArgType.Integer:
                                    int iarg;
                                    if(token.Text.StartsWith("0x"))
                                        iarg = Convert.ToInt32(token.Text.Substring(2), 16);
                                    else
                                        iarg = int.Parse(token.Text);
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(iarg)), 0, Code, CodeLength, 4);
                                    CodeLength += 4;
                                    break;
                                case ArgType.Float:
                                    float farg = float.Parse(token.Text);
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(farg)), 0, Code, CodeLength, 4);
                                    CodeLength += 4;
                                    break;
                                case ArgType.String:
                                    int strl = token.Text.Length;
                                    Buffer.BlockCopy(Encoding.ASCII.GetBytes(token.Text), 0, Code, CodeLength, strl);
                                    CodeLength += strl;
                                    Code[CodeLength] = 0;
                                    CodeLength++;
                                    break;
                                case ArgType.StaticFrameOffset:
                                    int SFOoffset;
                                    if(IdentifierExists(token.Text))
                                    {
                                        SFOoffset = GlobalVariableOffsets[GlobalVariableIdentifiers.IndexOf(token.Text)];
                                    }
                                    else
                                    {
                                        SFOoffset = int.Parse(token.Text);
                                    }
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(SFOoffset)), 0, Code, CodeLength, 4);
                                    CodeLength += 4;
                                    break;
                                case ArgType.LocalFrameOffset:
                                    int LFOoffset;
                                    if (IdentifierExists(token.Text))
                                    {
                                        LFOoffset = LocalVariableOffsets[LocalVariableIdentifiers.IndexOf(token.Text)];
                                    }
                                    else
                                    {
                                        LFOoffset = int.Parse(token.Text);
                                    }
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(LFOoffset)), 0, Code, CodeLength, 4);
                                    CodeLength += 4;
                                    break;
                                case ArgType.CodeOffset:
                                    int coffset;
                                    if (LabelExists(token.Text))
                                    {
                                        coffset = Labels[token.Text];
                                        coffset -= NextInstructionOffset;
                                    }
                                    else
                                    {
                                        coffset = int.Parse(token.Text);
                                    }
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(coffset)), 0, Code, CodeLength, 4);
                                    CodeLength += 4;
                                    break;
                                case ArgType.UserFunctionIndex:
                                    int fnum;
                                    if (IdentifierExists(token.Text))
                                    {
                                        fnum = UserFunctionIdentifiers.IndexOf(token.Text);
                                    }
                                    else
                                    {
                                        fnum = int.Parse(token.Text);
                                    }
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(fnum)), 0, Code, CodeLength, 4);
                                    CodeLength += 4;
                                    break;
                                case ArgType.LibFunctionIndexByte:
                                    int lbindex = Array.IndexOf(Builtins, token.Text);
                                    byte libbyte;
                                    if (lbindex == -1)
                                    {
                                        libbyte = byte.Parse(token.Text);
                                    }
                                    else
                                    {
                                        libbyte = (byte)lbindex;
                                    }
                                    Code[CodeLength] = libbyte;
                                    CodeLength++;
                                    break;
                                case ArgType.LibFunctionIndexTwoByte:
                                    int lsindex = Array.IndexOf(Builtins, token.Text);
                                    short libword;
                                    if(lsindex == -1)
                                    {
                                        libword = short.Parse(token.Text);
                                    }
                                    else
                                    {
                                        libword = (short)lsindex;
                                    }
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(libword)), 0, Code, CodeLength, 2);
                                    CodeLength += 2;
                                    break;
                                case ArgType.StateIndex:
                                    int sindex = StateIdentifiers.IndexOf(token.Text);
                                    if (sindex == -1)
                                    {
                                        sindex = int.Parse(token.Text);
                                    }
                                    Buffer.BlockCopy(Helpers.BigEndian(BitConverter.GetBytes(sindex)), 0, Code, CodeLength, 4);
                                    CodeLength += 4;
                                    break;
                                default:
                                    break;
                            }
                            ExpectedArgIndex++;
                            if (ExpectedArgIndex < ExpectedArgTypes.Length)
                            {
                                BeginSubstage(AssemblerSubstage.Event_Instruction_Args);
                            }
                            else
                            {
                                BeginSubstage(AssemblerSubstage.Event_Innards);
                            }

                        }
                    }

                }
                else if ((token.Type & ExpectedForSubstageChange) != 0)
                {
                    if (Stage == AssemblerStage.Globals)
                    {
                        // All substage changes are handled for this, so change stage
                        switch (token.Type)
                        {
                            case TokenType.StateDefault:
                                CalculateGlobalVariableOffsets();
                                BeginStage(AssemblerStage.DefaultState);
                                break;
                            default:
                                break;
                        }
                    }
                    else if(Stage == AssemblerStage.State || Stage == AssemblerStage.StateContinue)
                    {
                        if (Substage == AssemblerSubstage.State_Keyword)
                        {
                            if (token.Type == TokenType.EOF)
                            {
                                Log("EOF reached ok...");
                                FinishAssembly();
                                return true;
                            }
                        }
                        else if (Substage == AssemblerSubstage.State_Next_EventHandler)
                        {
                            // State finished here?
                            FinishState();
                            BeginStage(AssemblerStage.State);
                        }
                    }
                    else if (Stage == AssemblerStage.EventHandler)
                    {
                        if (Substage == AssemblerSubstage.Event_Arg_Type)
                            BeginSubstage(AssemblerSubstage.Event_Begin);
                        else if (Substage == AssemblerSubstage.Event_Begin)
                            BeginSubstage(AssemblerSubstage.Event_Innards);
                        else if (Substage == AssemblerSubstage.Event_Innards)
                        {
                            CodeForAssembly chunk = new CodeForAssembly();
                            chunk.CallFrameSize = ThisCallFrameSize;
                            chunk.Code = new byte[CodeLength];
                            Buffer.BlockCopy(Code, 0, chunk.Code, 0, CodeLength);
                            FinishedCodeChunks.Add(ThisEvent, chunk);
                            BeginStage(AssemblerStage.StateContinue);
                        }
                    }
                }
                return true;
            }
        }

        public bool Assemble(string code)
        {
            int i;
            float f;
            string[] lines = code.Split("\n".ToCharArray());
            string temps;
            string[] tempsarray;
            bool instring;
            string stringtoken = "";
            List<Token> tokens;
            int numlines = lines.Length;
            for (int line = 0; line < numlines; line++)
            {
                tokens = new List<Token>();
                lines[line] = lines[line].Trim();

                // Remove quoted
                i = lines[line].IndexOf("//");
                if (i != -1)
                    lines[line] = lines[line].Substring(0, i);

                // Separate line into tokens
                temps = lines[line].Replace("\"", " \" ");
                temps = temps.Replace("(", " ( ");
                temps = temps.Replace(")", " ) ");
                temps = temps.Replace("<", " < ");
                temps = temps.Replace(">", " > ");
                tempsarray = temps.Split(" ,\t".ToCharArray());
                instring = false;
                stringtoken = "";
                foreach (string s in tempsarray)
                {
                    if (s == "\"")
                    {
                        instring = !instring;
                        if (!instring)
                        {
                            stringtoken = stringtoken.Replace(" ( ", "(");
                            stringtoken = stringtoken.Replace(" ) ", ")");
                            stringtoken = stringtoken.Replace(" < ", ">");
                            stringtoken = stringtoken.Replace(" < ", ">");
                            stringtoken = stringtoken.Replace("\\n", "\n");
                            stringtoken = stringtoken.Replace("\\t", "\t");
                            stringtoken = stringtoken.Replace("\\\\", "\\");

                            tokens.Add(new Token(TokenType.String, stringtoken));
                            stringtoken = "";
                        }
                    }
                    else
                    {
                        if (!instring)
                        {
                            // Normal ole tokens
                            if (s.Length > 0)
                                tokens.Add(new Token(TokenType.Unknown, s));
                        }
                        else
                        {
                            if (stringtoken == "")
                                stringtoken = s;
                            else
                                stringtoken += " " + s;
                        }
                    }
                }
                if (instring)
                {
                    ErrorMessage = "String with no matching quote at line " + (line + 1).ToString();
                    return false;
                }

                // Type each token
                foreach(Token t in tokens)
                {
                    if(t.Text.Length != 0)

                    if (t.Type == TokenType.Unknown)
                    {
                        if (TypeByName.ContainsKey(t.Text))
                            t.Type = TokenType.Type;
                        else if (t.Text == "=")
                            t.Type = TokenType.Assignment;
                        else if (t.Text == "<")
                            t.Type = TokenType.VectorBegin;
                        else if (t.Text == ">")
                            t.Type = TokenType.VectorEnd;
                        else if (t.Text == "default")
                            t.Type = TokenType.StateDefault;
                        else if (t.Text == "state")
                            t.Type = TokenType.StateKeyword;
                        else if (t.Text == "{")
                            t.Type = TokenType.ScopeBegin;
                        else if (t.Text == "}")
                            t.Type = TokenType.ScopeEnd;
                        else if (t.Text == "(")
                            t.Type = TokenType.EventArgsBegin;
                        else if (t.Text == ")")
                            t.Type = TokenType.EventArgsEnd;
                        else if (t.Text.StartsWith("."))
                            t.Type = TokenType.Label;
                        else if (EventByName.ContainsKey(t.Text))
                            t.Type = TokenType.EventName;
                        else if (LSO.InstructionByName(t.Text) != null)
                        {
                            t.Type = TokenType.Instruction;
                            t.Text = t.Text.ToUpper();
                        }
                        else
                        {
                            if (int.TryParse(t.Text, out i))
                                t.Type = TokenType.Literal;
                            else if (float.TryParse(t.Text, out f))
                                t.Type = TokenType.Literal;
                            else
                                t.Type = TokenType.Identifier;
                        }
                    }

                    Log("Token type '" + t.Type.ToString() + "' : '" + t.Text + "'");
                    if (ReceiveToken(t) == false)
                    {
                        ErrorMessage += " at line " + (line + 1).ToString();
                        return false;
                    }
                }
            }
            if (ReceiveToken(new Token(TokenType.EOF, "EOF")) == false)
            {
                return false;
            }
            return true;
        }

        public LSOAssembler()
        {
            foreach (FieldInfo fi in typeof(VariableType).GetFields())
            {
                TypeByName.Add(fi.Name.ToLower(), (VariableType)fi.GetValue(new VariableType()));
            }
            foreach (FieldInfo fi in typeof(Events).GetFields())
            {
                EventByName.Add(fi.Name.ToLower(), (Events)fi.GetValue(new Events()));
            }

            Builtins = new string[] {
                // 0 - 15
                "llSin", "llCos", "llTan", "llAtan2",
                "llSqrt", "llPow", "llAbs", "llFabs",
                "llFrand", "llFloor", "llCeil", "llRound",
                "llVecMag", "llVecNorm", "llVecDist", "llRot2Euler",
                // 16 - 31
                "llEuler2Rot", "llAxes2Rot", "llRot2Fwd", "llRot2Left",
                "llRot2Up", "llRotBetween", "llWhisper", "llSay",
                "llShout", "llListen", "llListenControl", "llListenRemove",
                "llSensor", "llSensorRepeat", "llSensorRemove", "llDetectedName",
                // 32 - 47
                "llDetectedKey", "llDetectedOwner", "llDetectedType", "llDetectedPos",
                "llDetectedVel", "llDetectedGrab", "llDetectedRot", "llDetectedGroup",
                "llDetectedLinkNumber", "llDie", "llGround", "llCloud",
                "llWind", "llSetStatus", "llGetStatus", "llSetScale",
                // 47 - 63
                "llGetScale", "llSetColor", "llGetAlpha", "llSetAlpha",
                "llGetColor", "llSetTexture", "llScaleTexture", "llOffsetTexture",
                "llRotateTexture", "llGetTexture", "llSetPos", "llGetPos",
                "llGetLocalPos", "llSetRot", "llGetRot", "llGetLocalRot",
                // 64 - 79
                "llSetForce", "llGetForce", "llTarget", "llTargetRemove",
                "llRotTarget", "llRotTargetRemove", "llMoveToTarget", "llStopMoveToTarget",
                "llApplyImpulse", "llApplyRotationalImpulse", "llSetTorque", "llGetTorque",
                "llSetForceAndTorque", "llGetVel", "llGetAccel", "llGetOmega",
                // 80 - 95
                "llGetTimeOfDay", "llGetWallclock", "llGetTime", "llResetTime",
                "llGetAndResetTime", "llSound", "llPlaySound", "llLoopSound",
                "llLoopSoundMaster", "llLoopSoundSlave", "llPlaySoundSlave", "llTriggerSound",
                "llStopSound", "llPreloadSound", "llGetSubString", "llDeleteSubString",
                // 96 - 111
                "llInsertString", "llToUpper", "llToLower", "llGiveMoney",
                "llMakeExplosion", "llMakeFountain", "llMakeSmoke", "llMakeFire",
                "llRezObject", "llLookAt", "llStopLookAt", "llSetTimerEvent",
                "llSleep", "llGetMass", "llCollisionFilter", "llTakeControls",
                // 112 - 127
                "llReleaseControls", "llAttachToAvatar", "llDetachFromAvatar", "llTakeCamera",
                "llReleaseCamera", "llGetOwner", "llInstantMessage", "llEmail",
                "llGetNextEmail", "llGetKey", "llSetBuoyancy", "llSetHoverHeight",
                "llStopHover", "llMinEventDelay", "llSoundPreload", "llRotLookAt",
                // 128 - 143
                "llStringLength", "llStartAnimation", "llStopAnimation", "llPointAt",
                "llStopPointAt", "llTargetOmega", "llGetStartParameter", "llGodLikeRezObject",
                "llRequestPermissions", "llGetPermissionsKey", "llGetPermissions", "llGetLinkNumber",
                "llSetLinkColor", "llCreateLink", "llBreakLink", "llBreakAllLinks",
                // 144 - 159
                "llGetLinkKey", "llGetLinkName", "llGetInventoryNumber", "llGetInventoryName",
                "llSetScriptState", "llGetEnergy", "llGiveInventory", "llRemoveInventory",
                "llSetText", "llWater", "llPassTouches", "llRequestAgentData",
                "llRequestInventoryData", "llSetDamage", "llTeleportAgentHome", "llModifyLand",
                // 160 - 175
                "llCollisionSound", "llCollisionSprite", "llGetAnimation", "llResetScript",
                "llMessageLinked", "llPushObject", "llPassCollisions", "llGetScriptName",
                "llGetNumberOfSides", "llAxisAngle2Rot", "llRot2Axis", "llRot2Angle",
                "llAcos", "llAsin", "llAngleBetween", "llGetInventoryKey",
                // 176 - 191
                "llAllowInventoryDrop", "llGetSunDirection", "llGetTextureOffset", "llGetTextureScale",
                "llGetTextureRot", "llSubStringIndex", "llGetOwnerKey", "llGetCenterOfMass",
                "llListSort", "llGetListLength", "llList2Integer", "llList2Float",
                "llList2String", "llList2Key", "llList2Vector", "llList2Rot",
                // 192 - 207
                "llList2List", "llDeleteSubList", "llGetListEntryType", "llList2CSV",
                "llCSV2List", "llListRandomize", "llList2ListStrided", "llGetRegionCorner",
                "llListInsertList", "llListFindList", "llGetObjectName", "llSetObjectName",
                "llGetDate", "llEdgeOfWorld", "llGetAgentInfo", "llAdjustSoundVolume",
                // 208 - 223
                "llSetSoundQueueing", "llSetSoundRadius", "llKey2Name", "llSetTextureAnim",
                "llTriggerSoundLimited", "llEjectFromLand", "llParseString2List", "llOverMyLand",
                "llGetLandOwnerAt", "llGetNotecardLine", "llGetAgentSize", "llSameGroup",
                "llUnsit", "llGroundSlope", "llGroundNormal", "llGroundContour",
                // 224 - 239
                "llGetAttached", "llGetFreeMemory", "llGetRegionName", "llGetRegionTimeDilation",
                "llGetRegionFPS", "llParticleSystem", "llGroundRepel", "llGiveInventoryList",
                "llSetVehicleType", "llSetVehicleFloatParam", "llSetVehicleVectorParam", "llSetVehicleRotationParam",
                "llSetVehicleFlags", "llRemoveVehicleFlags", "llSitTarget", "llAvatarOnSitTarget",
                // 240 - 255
                "llAddToLandPassList", "llSetTouchText", "llSetSitText", "llSetCameraEyeOffset",
                "llSetCameraAtOffset", "llDumpList2String", "llScriptDanger", "llDialog",
                "llVolumeDetect", "llResetOtherScript", "llGetScriptState", "llRemoteLoadScript",
                "llSetRemoteScriptAccessPin", "llRemoteLoadScriptPin", "llOpenRemoteDataChannel", "llSendRemoteData",
                // 256 - 271
                "llRemoteDataReply", "llCloseRemoteDataChannel", "llMD5String", "llSetPrimitiveParams",
                "llStringToBase64", "llBase64ToString", "llXorBase64Strings", "llRemoteDataSetRegion",
                "llLog10", "llLog", "llGetAnimationList", "llSetParcelMusicURL",
                "llGetRootPosition", "llGetRootRotation", "llGetObjectDesc", "llSetObjectDesc",
                // 272 - 287
                "llGetCreator", "llGetTimestamp", "llSetLinkAlpha", "llGetNumberOfPrims",
                "llGetNumberOfNotecardLines", "llGetBoundingBox", "llGetGeometricCenter", "llGetPrimitiveParams",
                "llIntegerToBase64", "llBase64ToInteger", "llGetGMTclock", "llGetSimulatorHostname",
                "llSetLocalRot", "llParseStringKeepNulls", "llRezAtRoot", "llGetObjectPermMask",
                // 288 - 303
                "llSetObjectPermMask", "llGetInventoryPermMask", "llSetInventoryPermMask", "llGetInventoryCreator",
                "llOwnerSay", "llRequestSimulatorData", "llForceMouselook", "llGetObjectMass",
                "llListReplaceList", "llLoadURL", "llParcelMediaCommandList", "llParcelMediaQuery",
                "llModPow", "llGetInventoryType", "llSetPayPrice", "llGetCameraPos",
                // 304 - 319
                "llGetCameraRot", "llSetPrimURL", "llRefreshPrimURL", "llEscapeURL",
                "llUnescapeURL", "llMapDestination", "llAddToLandBanList", "llRemoveFromLandPassList",
                "llRemoveFromLandBanList", "llSetCameraParams", "llClearCameraParams", "llListStatistics",
                "llGetUnixTime", "llGetParcelFlags", "llGetRegionFlags", "llXorBase64StringsCorrect",
                // 320 - 335
                "llHTTPRequest", "llResetLandBanList", "llResetLandPassList", "llGetParcelPrimCount",
                "llGetParcelPrimOwners", "llGetObjectPrimCount", "llGetParcelMaxPrims", "llGetParcelDetails"
            };

            BeginStage(AssemblerStage.Globals);
        }
    }
}
